package com.mcookie.app.minicompo;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.BlurMaskFilter.Blur;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class MiniCompoView extends View{
	
	//----------------페인트영역----------------//
	
	Paint picturePaint;
	Paint mainPaint;
	static Paint erasePaint;
	
	 //----------------캔버스영역----------------//
	
	static Canvas buffCanvas;
	
	//----------------비트맵영역----------------//
	
	static Bitmap buffBitmap; //버퍼.
	static Bitmap baseBitmap; //베이스.
	static Bitmap compoBitmap; //합성.
	Bitmap tempB; //임시.
	
	//----------------좌표영역----------------//
	float fristX, fristY; //터치시 다운좌표.
	
	float GapLeft = 0;
	float GapTop = 0;
	
	static float preGapLeft = 0; 
	static float preGapTop = 0;
	
	static float preDistance = 0;
	
	static float scale = 1;
	
	float preAngle = 0;
	float postAngle;
	float angle = 0;
	
	int centerX; //중앙좌표.
	int centerY;
	
	//----------------모드영역----------------//
	final int MODE_MOVE = 100;
	final int MODE_ERASE = 101;
	static int MODE;
	
	//----------------기타영역----------------//
	Context mContext;
	int windowW = 400;
	int windowH = 500;
	
	static Matrix buffMatrix;
	Matrix buffMatrixA = new Matrix();
	Matrix buffMatrixB = new Matrix();
	
	
	static boolean aa = true;
	
	boolean isMult = false;//멀티터치 여부 판별하기.
	boolean postMult = false;
	
	
	
	public MiniCompoView(Context context) {
		super(context);
		mContext = context;
		init();
	}
	
	

	public MiniCompoView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mContext = context;
		init();
	}

	/**
	 * 뷰 초기화메소드.
	 */
	public void init() {
		
		//버퍼 메트릭스 셋. 
		buffMatrix = new Matrix();
		buffMatrix.setTranslate(0, 0);
		
		
		//메인페인트 셋.
		mainPaint = new Paint();
		mainPaint.setColor(Color.RED);
		
		//모드 초기값.
		MODE = MODE_MOVE;
		
		//지우개용 페인트 셋
		erasePaint = new Paint();
		erasePaint.setAntiAlias(true);
		erasePaint.setColor(0x00FF0000);
		erasePaint.setStyle(Paint.Style.STROKE);
		erasePaint.setStrokeJoin(Paint.Join.ROUND);
		erasePaint.setStrokeCap(Paint.Cap.ROUND);
		erasePaint.setStrokeWidth(50);
		erasePaint.setXfermode(null); //지우개모드 초기값 null
		
		//필터블러.
		erasePaint.setMaskFilter(null);
		
		picturePaint = new Paint();
        
	}
	
	/**
	 * 메인액티비티에서 잘라낸 합성이미지를 셋팅한다.
	 * @param bm
	 */
	public void setCompoImage(Bitmap bm){
		
		buffCanversCreate();
		
		buffCanvas.drawBitmap(bm, 0, 0, null);
		buffCanvas.drawColor(0xFF00000);
		
		Log.d("june2", "setCompoImage : " + bm.getRowBytes());
		
		invalidate();
	}
	
	
	/**
	 * 브러쉬를 부드럽게 처리할지, 각지게 처리할지를 셋팅한다.
	 * @param blur
	 */
	public void setErasePaint(Blur blur){
		
		if(blur != null){
			BlurMaskFilter blurMaskFilter = new BlurMaskFilter(20, blur);
			erasePaint.setMaskFilter(blurMaskFilter);
		} else { 
			erasePaint.setMaskFilter(null);
		}
		
		
	}
	
	
	/**
	 * 버퍼비트맵을 생성하고 캔버스에 셋팅.
	 * 셋 비트맵이 이루어지면 버퍼캔버스가 비트맵의 변화를 감지, 실시간 반응한다.
	 */
	private void buffCanversCreate() {
		// TODO Auto-generated method stub
		buffBitmap = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
        buffCanvas = new Canvas();
        buffCanvas.setBitmap(buffBitmap);
	}

	/**
	 * 무브모드와 지우개모드를 교체한다.
	 * @param mode 100 = move, 101 = erase 
	 */
	public void modeChange(int mode){
		this.MODE = mode;
	}

	
	/**
	 * 지우개 모드 변경시 페인트에 xfermode값을 clean으로 변경한다.
	 * @param pdfx
	 */
	public void pdfxChange(Object pdfx){
		
		Log.d("june", "pdfxChange 호출");
		
		if(pdfx == null){
			erasePaint.setXfermode(null);
		} else {
			erasePaint.setStyle(Style.FILL);
			erasePaint.setXfermode((PorterDuffXfermode)pdfx);
		}
	}
	
	
	 
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		
		//배경색.
		canvas.drawColor(Color.BLACK);
		
		//베이스이미지 셋팅.
		if(baseBitmap != null){
			canvas.drawBitmap(baseBitmap, 0, 35, mainPaint);
			Log.d("june", "baseBitmap != null");
		}
		
		//버퍼비트맵. 중요..... 여기서 사용한 버퍼메트릭스에 오류의 모든것이 들어있다....
		if(buffBitmap != null){
			canvas.drawBitmap(buffBitmap, buffMatrix, mainPaint);
		}
	}
	
	
	
	/**
	 * isMult와 postMult 값을 이용하여 멀티터치 발생 이후 한손가락이 떨어졌을경우
	 * 터치이벤트가 동작하지 못하도록 막아두었다. 재동작을 위해서 두 손가락 모두 떨어졌다가 다시 터치해야한다.
	 */
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		
		//터치좌표.
		float x = event.getX();
		float y = event.getY();
	
		
		if(MODE == MODE_ERASE){
			//터치이벤트의 각 반응별 브러쉬를 설정한다.
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				
				//터치시 첫번째 좌표점을 보존한다. 
				fristX = x;
				fristY = y;
				invalidate();
				
				break;
				
			case MotionEvent.ACTION_MOVE:
				 
				//센터점을 기준으로 잡았을때 이상동작을 보이고있다. 센터점 없이 0,0 을 좌표로 잡으면 틀어지지는 않는다.
				//지우개로 지워질 범위. 사이클로 표기하면서 실시간 지우기는 가능. 근데 빨리 움직일때 사이에 갭이 생긴다...
				Log.d("june", "x : " + x + ", preGapLeft : " + preGapLeft + ", centerX : " + centerX + ", centerY" + y + 
						", preGapTop : " + preGapTop + ", centerY : " + centerY + ", scale : "+ scale + "postAngle : " + postAngle);
				
				//스케일값을 감안하여 포인트를 회전한다. 즉 이 과정이 처리되고나면 캔버스가 돌았건, 스케일이 조절됬건 정확한 좌표에서 처리되어야한다.
				//안쪽에보면 지워지기위해 겹쳐지는 원이 들어있다. (erasePaint를 쓴 사이클);
				rotatePoint((x - preGapLeft)/scale, (y - preGapTop)/scale, centerX, centerY, postAngle);
				  
				invalidate();
				 
				break;
				
			//싱글 업은 처리하지 않는다. 실시간처리이기때문에 모둔 작업은 무브로...
			case MotionEvent.ACTION_UP:
				break;
			}
		}
		

		
		
		
		//이동모드. 여기서 멀티터치를 이용한 스케일 및 로테이트. 싱글터치 무브가 구현된다.
		if(MODE == MODE_MOVE){
			
			//버퍼캔버스 널체크. 합성이미지가 올라왔을때에만 한하여 작업을 처리한다.
			if(buffCanvas !=null){
				
				switch (event.getAction()) {
				
				case MotionEvent.ACTION_DOWN:
					
					//멀티터치 판별 플래그.
					isMult = false;
					//멀티터치이후 하나의 손가락만 업되었을때 싱글터치가 동작하면서 오작동 발생.
					//해당 오류를 막기위해 플래그를 따로 설정했다.
					postMult = true;
					
					//최초 터치값.
					fristX = x;
					fristY = y;
					
					break;
				
					
				case MotionEvent.ACTION_MOVE:
					
					//멀티터치일때. 
					if(isMult){
						Log.d("june", "멀티터치동작중");
						
						//거리값. 현재 멀티터치에서 첫번째 x,y 와 두번째 x,y좌표를 받아서 직선으로 거리를 구한다.
						float distance = spacing(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
						//이미 구해져있는 최초 멀티터치 거리값을 이용하여 스케일값을 구한다.
						scale = distance / preDistance;
						
						//스케일 제한. 0.5 ~ 2.0까지를 범위로 잡는다.범위 이하, 이상일때에는 아웃오브메모리 발생확율이 높다.
						if(scale <= 0.5f){
							scale = 0.5f;
						}
						 
						if(scale >= 2){
							scale = 2;
						}
						
						
						Log.e("june", "멀티터치작동중 스케일값 : " +scale);
						  
						//멀티터치 좌표를 이용하여 직선을 그어 각도를 반환한다.
						angle = getDegreeFromCartesian(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
						//이미 구해져있는 최조각도를 이용하여 변경각을 잡아낸다.
						postAngle = angle - preAngle;
	
						//스케일 셋팅.
						buffMatrixA.setScale(scale, scale, centerX, centerY);
						//회전각 셋팅.
						buffMatrixB.reset();
						buffMatrixB.postRotate(postAngle, centerX, centerY);
						
						//두 메트릭스를 하나로. 하나의 메트릭스만 사용하여  set할때에는 오류발생.
						buffMatrix.setConcat(buffMatrixA, buffMatrixB);
						//메트릭스에 이동값 셋팅.
						buffMatrix.postTranslate(GapLeft, GapTop);
						 
					}
					 
					
					//멀티터치가 아닐때. 즉 싱글터치랍니다.
					else {
						
						//멀티터치 이후 한손가락만 떨어졌을때 동작을 방지하기 위한 조건문.
						if(postMult){
							
							Log.d("june", "싱글터치동작중");
							
							//현재위치에서 얼마나 이동했는지 이동값의 차이를 구한다.
							GapLeft = preGapLeft + (x - fristX);
							GapTop = preGapTop + (y - fristY);
							
							Log.d("june", "싱글터치동작중 스케일값 : " + scale);
							
							//미묘하다... 
							buffMatrix.reset();
							buffMatrixA.setScale(scale, scale, centerX, centerY);
							buffMatrixB.setRotate(postAngle, centerX, centerY);
							buffMatrix.setConcat(buffMatrixA, buffMatrixB);
							buffMatrix.postTranslate(GapLeft, GapTop);
							
						}
						
					} 
					
					invalidate();
					break;
				
					
				case MotionEvent.ACTION_UP:
					
					Log.d("june", "싱글터치업");
					
					if(!isMult){
						
						Log.d("june2", "preGapLeft 이 갱신되고있다!");
						//업되었을떄의 갭값 저장.
						preGapLeft = GapLeft;
						preGapTop = GapTop;
					}
					
					//멀티터치상태 해제.
					isMult = false;
					invalidate();
					break;
					
				//두번째 터치된 손가락의 다운이벤트.
				case MotionEvent.ACTION_POINTER_2_DOWN:
					isMult = true;
					postMult = false;
					
					//센터포인트를 설정한다. 첫번째 터치와 두번째 터치사이, 중앙값을 잡도록 셋팅.
					//붉은색 점으로 표시된다.
					Point centerPoint = centerPoint(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
					
					//반환된 센터 좌표.
					centerX = centerPoint.x;
					centerY = centerPoint.y;
					
					//터치직후 구해지는 첫번째 거리값. 이후 스케일 공식에서 사용한다.
					preDistance = spacing(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
					
					
					//최초 회전각 구하기.
					if(preAngle == 0){
						preAngle = getDegreeFromCartesian(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
					} else {
						preAngle = postAngle;
					}
					
					
					Log.d("june", "포인트 투 터치다운");
					break;
				
					
				case MotionEvent.ACTION_POINTER_1_UP:
					
					//먼저 찍힌 포인트가 먼저 떨어졌을때 호출된다.
					Log.d("june", "포인트 원 터치업");
					Log.e("june", "포인트 원 터치업 앵글값 : " + postAngle);
					isMult = false;

					break;
	 
					
				case MotionEvent.ACTION_POINTER_2_UP:
					
					//나중에 찍힌 포인트가 먼저 떨어질때 호출된다. 
					Log.d("june", "포인트 투 터치업");
					isMult = false;
					
					break;
				}

			}
		}
		
		
		return true;
	}
	
	/**
	 * 터치이벤트에서 이벤트만 따서 싱글터치와 멀티터치간의 거리를 측정해서 반환한다.
	 * @param event 터치이벤트의 이벤트. 주의할건 그냥 액션다운에서는 1번좌표를 인식하지 못한다는것이다.
	 * 				이를 위해서 액션 포인트 다운에서 측정해야한다.(있는줄도 몰랐다...)
	 * 				액션 포인트 다운은 두번째 터치가 이루어졌을때의 다운이벤트이다.
	 * @return 첫좌표와 두번째 좌표의 거리.
	 */
	private float spacing(float nowX, float nowY, float centerX, float centerY) {
	    float x = nowX - centerX;
	    float y = nowY - centerY;
	    Log.d("distans return ", "distans return : " + FloatMath.sqrt(x * x + y * y)); 
	    return FloatMath.sqrt(x * x + y * y);
	   
	}
	
	
	/**
	 * @param nowX event.getX(0) : 첫번째 터치시의 x좌표입니다.
	 * @param nowY event.getY(0) : 첫번째 터치시의 y좌표입니다.
	 * @param centerX event.getX(1) : 두번째 터치시의 x좌표입니다.
	 * @param centerY event.getY(1) : 두번째 터치시의 y좌표입니다.
	 * @return 이걸로 두 점을 이은 실선의 각도가 구해졌습니다. 짠!
	 */
	private float getDegreeFromCartesian(float nowX, float nowY, float centerX, float centerY)
	{
		 
		
		float angle = (float) Math.atan2((centerX - nowX), (centerY-nowY));
		float angleindegree = (float) (angle * 180/Math.PI);
		
		return 180-angleindegree;
		
	}

	/**
	 * 회전변환식. 참조했던 수식이 망나니수식. 수학책을 뒤적거려서 짜냈다. 결과는 제대로 반환된다.
	 * 회전각의 중심좌표를 파란점으로 표기한다.
	 * @param rotatePX 회전할 포인트.
	 * @param rotatePY 회전할 포인트.
	 * @param oriX 기준 좌표.
	 * @param oriY 기준 좌표.
	 * @param radian 회전할 각도.
	 * @return
	 */
	public Point rotatePoint(float rotatePX, float rotatePY, float oriX, float oriY, float radian){
		
		Log.e("june", oriX + ", " + oriY + ", " + rotatePX + ", " + rotatePY + ", " + radian);
		
		//중심좌표.
		Paint paint = new Paint();
		paint.setColor(Color.BLUE);
		buffCanvas.drawCircle(oriX, oriY, 10, paint);
		
		//360도 차익. 설정된 회전각을 반시계방향으로 돌린다.
		radian = (360-radian);
		
		float r = (float) (radian*Math.PI/180);
		
		float postRPX = (float) (((rotatePX-oriX)*Math.cos(r) - (rotatePY-oriY)*Math.sin(r))+oriX);
		float postRPY = (float) (((rotatePX-oriX)*Math.sin(r) + (rotatePY-oriY)*Math.cos(r))+oriY);
		
		Point ReturnPoint = new Point((int)postRPX, (int)postRPY); 
         
		//erasePaint를 통해 그려지게되면 합성이미지와 겹쳐질경우 해당 범위가 지워진다. 
		//메인 엑티비티에서 라디오버튼이 클릭되면 pdfxChange 메소드를 통과하여 지우개모드가 셋팅된다.
		buffCanvas.drawCircle(ReturnPoint.x, ReturnPoint.y, MiniCompoActivity.BlurSize, erasePaint);
		
        return ReturnPoint;
	}
	
	/**
	 * 중심좌표 알아내기. 이 좌표를 기준으로 스케일과 회전을 진행할 생각.
	 * 첫번째 터치와 두번째 터치시의 좌표를 기준으로 그 사이 점을 포인트로 반환한다.
	 * @param fX 첫번채 터치 x
	 * @param fY 첫번째 터치 y
	 * @param sX 두번째 터치 x
	 * @param sY 두버쨰 터치 y
	 * @return 센터포인트. 쓸때는 .x, .y로 불러올수있다.
	 */
	public Point centerPoint(float fX, float fY, float sX, float sY){
		
		Log.e("june", fX + ", " + fY + ", " + sX + ", " + sY);
		
		Point centerPoint = new Point();
		
		if(fX > sX){
			centerPoint.x = (int)(((fX-sX)/2)+sX-preGapLeft);
		} else {
			centerPoint.x = (int)(((sX-fX)/2)+fX-preGapLeft);
		}
		
		if(fY > sY){
			centerPoint.y = (int)(((fY-sY)/2)+sY-preGapTop);
		} else {
			centerPoint.y = (int)(((sY-fY)/2)+fY-preGapTop);
		}
		
		//찾아낸 중심좌표. 빨간색.
		buffCanvas.drawCircle(centerPoint.x, centerPoint.y, 5, mainPaint);
		return centerPoint;
	}
}




//----------자료백업-----------//
/*@Override
public boolean onTouchEvent(MotionEvent event) {
	
	float x = event.getX();
	float y = event.getY();
	
	if(MODE == MODE_ERASE){
		//터치이벤트의 각 반응별 브러쉬를 설정한다.
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			
			fristX = x;
			fristY = y;
			invalidate();
			
			break;
			
		case MotionEvent.ACTION_MOVE:
			//지우개로 지워질 범위. 사이클로 표기하면서 실시간 지우기는 가능. 근데 빨리 움직일때 사이에 갭이 생긴다...
			buffCanvas.drawCircle(x-GapLeft, y-GapTop, 20, erasePaint);
			invalidate();
			
			break;
			
		case MotionEvent.ACTION_UP:
//			touchUp(x, y);
			break;
		}
	}
	
	if(MODE == MODE_MOVE){
		
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			
			fristX = x;
			fristY = y;
			
			buffMatrix.setTranslate(GapLeft, GapTop);
			
			
			break;
			
		case MotionEvent.ACTION_MOVE:
			
			
			moveCanvas(fristX, fristY, x, y);
			
			invalidate();
			
			break;
			
		case MotionEvent.ACTION_UP:

			GapLeft = x - fristX;
			GapTop = y - fristY;
			
//			buffCanvas.drawColor(Color.BLACK);
//			buffCanvas.translate(GapLeft, GapTop);
			
			invalidate();
			break;
		}
	}
	
	
	return true;
} */

/*
public void touchDown(float x, float y){
	 erasePath.reset();
	 erasePath.moveTo(x, y);
}

public void touchUp(float x, float y){
	
	erasePath.lineTo(fristX, fristY);
     // commit the path to our offscreen
	buffCanvas.drawPath(erasePath, erasePaint);
     
     // kill this so we don't double draw
    erasePath.reset();
}

private static final float TOUCH_TOLERANCE = 4;
public void touchMove(float x, float y) {
	
	 float dx = Math.abs(x - fristX);
     float dy = Math.abs(y - fristY);
     if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
    	 erasePath.quadTo(fristX, fristY, (x + fristX)/2, (y + fristY)/2);
         fristX = x;
         fristY = y;
     }
}*/



/**
 * 이미지 이동하기. 버퍼캔버스에 대한 트렌슬레이트 처리.
 * @param fristX
 * @param fristY
 * @param x
 * @param y
 */
/*
public void moveCanvas(float fristX, float fristY, float x, float y){
	
	Log.d("june", "moveCanvas 호출");
	
	float Left = GapLeft + (x - fristX);
	float Top = GapTop + (y - fristY);
	
	buffCanvas.drawCircle(Left, Top, 20, mainPaint);
	
	Log.d("june", "moveCanvas left : " + Left + ", top : " + Top);
	
	buffMatrix.reset();
	buffMatrix.postTranslate(Left, Top);
}*/


/**
 * 이미지 이동하기. 버퍼캔버스에 대한 트렌슬레이트 처리.
 * @param fristX
 * @param fristY
 * @param x
 * @param y
 */
/*
public void moveCanvas(float fristX, float fristY, float x, float y){
	
	Log.d("june", "moveCanvas 호출");
	
	float Left = GapLeft + (x - fristX);
	float Top = GapTop + (y - fristY);
	
	Log.d("june", "moveCanvas left : " + Left + ", top : " + Top);
	
	buffMatrix.reset();
	buffMatrix.postTranslate(Left, Top);
	
}*/